// directory.cc 
//	Routines to manage a directory of file names.
//
//	The directory is a table of fixed length entries; each
//	entry represents a single file, and contains the file name,
//	and the location of the file header on disk.  The fixed size
//	of each directory entry means that we have the restriction
//	of a fixed maximum size for file names.
//
//	The constructor initializes an empty directory of a certain size;
//	we use ReadFrom/WriteBack to fetch the contents of the directory
//	from disk, and to write back any modifications back to disk.
//
//	Also, this implementation has the restriction that the size
//	of the directory cannot expand.  In other words, once all the
//	entries in the directory are used, no more files can be created.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "utility.h"
#include "filehdr.h"
#include "directory.h"
//----------------------------------------------------------------------
// Directory::Directory
// 	Initialize a directory; initially, the directory is completely
//	empty.  If the disk is being formatted, an empty directory
//	is all we need, but otherwise, we need to call FetchFrom in order
//	to initialize it from disk.
//
//	"size" is the number of entries in the directory
//----------------------------------------------------------------------


Directory::Directory()
{
	fatherDirHdrSector = 1;
}

Directory::Directory(int size)
{
	DirectoryEntry de = {false};
	fatherDirHdrSector = 1;
	for(int i = 0; i < size; i ++)
	{
		childrenList.push_back(de);
	}
}
//----------------------------------------------------------------------
// Directory::~Directory
// 	De-allocate directory data structure.
//----------------------------------------------------------------------

Directory::~Directory()
{ } 

void
Directory::SetFatherDir(int sector)
{
	fatherDirHdrSector = sector;
}
int 
Directory::GetFatherDir()
{
	return fatherDirHdrSector;
}
//----------------------------------------------------------------------
// Directory::FetchFrom
// 	Read the contents of the directory from disk.
//
//	"file" -- file containing the directory contents
//----------------------------------------------------------------------
void
Directory::AddListNode(int size)
{
	DirectoryEntry de = {false};
	for(int i = 0; i < size; i ++)
	{
		childrenList.push_back(de);
	}
}
bool
Directory::FindAndAssign(DirectoryEntry de)
{
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if(!(*it).inUse)
		{
			(*it) = de;
			return true;
		}
	}
    return false;
}

void
Directory::FetchFrom(OpenFile *file)
{
	int numInList = 0;
	DirectoryEntry de;
	(void) file->ReadAt((char *)&numInList, sizeof(int), 0);
	for(int i = 0; i < numInList; i ++)
	{
    	(void) file->ReadAt((char *)&de, sizeof(DirectoryEntry), sizeof(int) + i*sizeof(DirectoryEntry));
		childrenList.push_back(de);
	}
}

//----------------------------------------------------------------------
// Directory::WriteBack
// 	Write any modifications to the directory back to disk
//
//	"file" -- file to contain the new directory contents
//----------------------------------------------------------------------

void
Directory::WriteBack(OpenFile *file)
{
	DirectoryEntry de;
	int    numInList = childrenList.size();
	(void) file->WriteAt((char *)&numInList, sizeof(int), 0);
	for(int i = 0; i < numInList; i ++)
	{	
		de = childrenList.front();
		childrenList.pop_front();
		(void) file->WriteAt((char *)&de, sizeof(DirectoryEntry), sizeof(int) + i*sizeof(DirectoryEntry));
	}
}

//----------------------------------------------------------------------
// Directory::FindIndex
// 	Look up file name in directory, and return its location in the table of
//	directory entries.  Return -1 if the name isn't in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------


//----------------------------------------------------------------------
// Directory::Find
// 	Look up file name in directory, and return the disk sector number
//	where the file's header is stored. Return -1 if the name isn't 
//	in the directory.
//
//	"name" -- the file name to look up
//----------------------------------------------------------------------

int
Directory::Find(char *name) 
{	
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if((*it).inUse)
		{
			if(!strncmp((*it).name, name, FileNameMaxLen))
			{	
				return (*it).sector; 
			}
		}
	}
    return -1;
}

//----------------------------------------------------------------------
// Directory::Add
// 	Add a file into the directory.  Return TRUE if successful;
//	return FALSE if the file name is already in the directory, or if
//	the directory is completely full, and has no more space for
//	additional file names.
//
//	"name" -- the name of the file being added
//	"newSector" -- the disk sector containing the added file's header
//----------------------------------------------------------------------

bool
Directory::Add(char *name, int newSector, bool isLeaf)
{ 
    if (Find(name) != -1)
		return FALSE;
	DirectoryEntry de = {true, isLeaf, newSector};
	strncpy(de.name, name, FileNameMaxLen);
	if(!FindAndAssign(de))
	{
		return FALSE;	
	}

	return TRUE;
}

//----------------------------------------------------------------------
// Directory::Remove
// 	Remove a file name from the directory.  Return TRUE if successful;
//	return FALSE if the file isn't in the directory. 
//
//	"name" -- the file name to be removed
//----------------------------------------------------------------------

bool 
Directory::IsLeaf(char *name)
{
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if(!strncmp((*it).name, name, FileNameMaxLen))
			return (*it).isLeaf; 
	}
}
bool 
Directory::IsInList(char *name)
{
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if(!strncmp((*it).name, name, FileNameMaxLen))
			return true; 
	}
	return false;
}
char** 
Directory::GetNames()
{
	int size = GetNumInUse();
	int i;
	char** names = new char*[size];

	for(i = 0; i < size; i ++)
		names[i] = new char[FileNameMaxLen + 1];

	i = 0;
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end() && i < size; it ++, i ++)
	{
		if((*it).inUse)
			 strncpy(names[i], (*it).name, FileNameMaxLen); 
	}
	return names;
}
int 
Directory::GetNumInUse()
{
	int num = 0;
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if((*it).inUse)
			num ++;
	}
	return num;
}

bool
Directory::Remove(char *name)
{ 
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{
		if(!strncmp((*it).name, name, FileNameMaxLen))
		{
			(*it).inUse = false;
		}
	}
	return true;	
}

//----------------------------------------------------------------------
// Directory::List
// 	List all the file names in the directory. 
//----------------------------------------------------------------------

void
Directory::List()
{
	for(list<DirectoryEntry>::iterator it = childrenList.begin(); it != childrenList.end(); it ++)
	{ 
		if((*it).inUse)
		{
			if((*it).isLeaf)
				printf("File	%s\n", (*it).name);
			else
				printf("Folder	%s\n", (*it).name);
		}
	}
	cout<<"father dir:" << fatherDirHdrSector<< endl;
}
//----------------------------------------------------------------------
// Directory::Print
// 	List all the file names in the directory, their FileHeader locations,
//	and the contents of each file.  For debugging.
//----------------------------------------------------------------------

void
Directory::Print()
{ 
    /*FileHeader *hdr = new FileHeader;

    printf("Directory contents:\n");
    for (int i = 0; i < tableSize; i++)
	if (table[i].inUse) {
	    printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
	    hdr->FetchFrom(table[i].sector);
	    hdr->Print();
	}
    printf("\n");
    delete hdr;*/
}
